"""
-------------------------------------------------
  Author:hxy
  Date:2022-09-06 14:47
  Des:sub
-------------------------------------------------
"""

# set PYTHONPATH=D:\pycharm\pywork\django-vue-admin_project\iot_project\backend
import sys
print('sys.path', sys.path)

import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings')
import django
django.setup()

from datetime import datetime
import threading
import paho.mqtt.client as mqtt
from threading import Thread
import logging
import pymssql
from iot.models import Gradation, Cabinet, Point, Unit
from common.tdengine import load_tdengine_pool
import json

_logger = logging.getLogger(__file__)
# host = '192.168.0.244'
host = '39.98.122.153'
TEMP_TYPE = 'T'
publish_topic = 'iot/temp/trans'

pg_lock = threading.Lock()

# 其目的是处理传感器数据，并根据特定的条件发布警告信息。
def trans(client, equipment, location, sensor, value, table):
    # 创建一个字符串 key，它由位置、设备和传感器名称连接而成，通常用于唯一标识一个传感器。
    key = location+'.'+equipment+'.'+sensor
    # 将传入的 value 四舍五入到小数点后两位，存储在 value_now 变量中。
    value_now = round(value, 2)
    # 创建一个字典 payload，其键为之前生成的 key，值为处理后的 value_now
    payload = {key: value_now}
    # 发布
    client.publish(publish_topic, json.dumps(payload, ensure_ascii=False))
    # 创建点位表对象
    points = Point.objects.filter(code=table)
    # 并不是外键就是单独的字段
    warm_warning = points.first().cabinet.warm_warning
    warm_early_warning = points.first().cabinet.warm_early_warning
    if warm_warning:
        # 如果传感器传过来的值大于报警值
        if value_now > warm_warning:
            # 发布传感器信息
            client.publish(publish_topic, json.dumps({key+'_warn': 1}, ensure_ascii=False))
            return
    if warm_early_warning:
        # 如果传感器的值大于预警值
        if value_now > warm_early_warning:
            # # 发布传感器信息
            client.publish(publish_topic, json.dumps({key+'_warn': 1}, ensure_ascii=False))
            return
    client.publish(publish_topic, json.dumps({key+'_warn': 0}, ensure_ascii=False))


class Sub(Thread):

    def __init__(self, topic, username, password, host, port, client_id):
        super().__init__()
        self.topic = topic
        self.client = mqtt.Client(client_id)
        self.client.username_pw_set(username, password)
        self.client.connect(host=host, port=port)
        self.client.on_connect = self.on_connect
        self.client.on_message = self.on_message
        self.state = True
        self.lt = None

    def on_connect(self, client, userdata, flags, rc):
        rc = str(rc)
        if rc == '0':
            self.client.subscribe(self.topic)
            print("链接成功")
        else:
            _logger.info(f'连接失败: 状态码为{rc}')

    def on_message(self, client, userdata, msg):
        try:
            # eval() 函数用来执行一个字符串表达式，并返回表达式的值。
            # 它将字节字符串解码为UTF-8格式的普通字符串
            data_list = eval(msg.payload.decode('utf-8'))
            # {"sensor": "d_T_A", "value": 0.0, "equipment": "L38", "location": "hanji"}
            # {
            #   'sensor': 'd_T_A',
            #   'value': 0.0,
            #   'equipment': 'L38',
            #   'location': 'hanji',
            #   'gradation': 'd',   分层
            #   'type': 'T',        类型温度的意思
            #   'ABC': 'A',         点位
            #   'time': <当前时间对象>
            # }

            # sensor:分u_T_C\m_T_C\d_T_C
            # u代表套管位置温度
            # m代表上触指
            # d代表下触指
            # A:A相
            # B：B相
            # C：C相
            # value：温度值
            # equipment：柜号


            if data_list:
                point = data_list['sensor']
                # point_list = ["m", "T", "C"]
                point_list = point.split('_')
                data_list['gradation'] = point_list[0]
                data_list['type'] = point_list[1]
                data_list['ABC'] = point_list[2]
                data_list['time'] = datetime.now()
                data_list['value'] = float('%.2f' % data_list['value'])
                t = threading.Thread(target=Sub.write_to_td, args=(self, data_list,))
                t.start()
        except Exception as e:
            _logger.error(f'e: {e}')

    def write_to_td(self, data_list):
        point = data_list['sensor']
        equipment = data_list['equipment']
        gradation = data_list['gradation']
        time = data_list['time']
        value = data_list['value']
        type = data_list['type']
        ABC = data_list['ABC']
        table = equipment + '_' + point
        Sub.write_to_pg(equipment, gradation, table, value, type, ABC, time)
        if type == TEMP_TYPE:
            location = data_list['location']
            trans(self.client, equipment, location, point, value, table)
        with load_tdengine_pool().get() as cursor:
            # 电柜号_point也就是taos子表名称
            # 创建超级表（如果表已存在，则会忽略该操作）
            # a = cursor.execute(
            #     "CREATE STABLE IF NOT EXISTS iot (ts TIMESTAMP, value FLOAT) TAGS (code NCHAR(256));"
            #     # "DESCRIBE iot;"
            # )
            # print(a)
            # 如果这个子表那么直接向下运行，如果这个子表不存在直接创建
            # location_tag_value = 'hanji_temp'  # 替换为实际的标签值
            super_table = "iot"
            # cursor.execute(
            #     f"""
            #     CREATE TABLE IF NOT EXISTS {table}
            #     USING {super_table} TAGS ('{equipment}')
            #     """
            # )
            # 插入数据
            sql = f"INSERT INTO {table} USING {super_table} TAGS ('{equipment}') VALUES ('{time}', '{value}');"
            print(sql)
            cursor.execute(sql)

    @staticmethod
    def write_to_pg(equipment, location, table, value, type, ABC, time):
        with pg_lock:
            print(location)
            cabinet = Cabinet.objects.filter(code=equipment)   # 电柜必须有匹配的（具体看mqtt）
            if cabinet.count() == 0:
                _logger.info('请配置相关配电站')
                return
            gradation = Gradation.objects.filter(code=location)  # 分层必须有匹配的（具体看MQTT发什么）
            if gradation.count() == 0:
                _logger.info('请配置相关电压、温度层级属性')
                return
            unit = Unit.objects.filter(type=type)   # 类型必有匹配的一般是T
            if unit.count() == 0:
                _logger.info('请配置相关单位类型')
                return

            points = Point.objects.filter(code=table)   # 点位表中必须有
            if points.count() == 0:
                # pass
                Point.objects.create(gradation=gradation.first(), unit=unit.first(),
                                     cabinet=cabinet.first(), **{'code': table, 'value': value,
                                                                 'name': ABC+gradation.first().name,
                                                                 'dept_belong_id': cabinet.first().dept_belong_id})
            else:
                Point.update(table, **{'value': value, 'update_datetime': time})

    # def write_to_server(self, sensor, value):
    #     conn = pymssql.connect(host, 'sa', '<Aa@23456>', 'Scada')
    #     cursor = conn.cursor()
    #     cursor.execute(f"select * from RTable WHERE tagname='{sensor}';")
    #     rows = cursor.fetchall()
    #     if len(rows) == 0:
    #         cursor.execute(f"insert into Rtable(tagname, tagvalue) values('{sensor}', {value});")
    #         conn.commit()
    #     else:
    #         cursor.execute(f"UPDATE RTable set tagvalue={value} WHERE tagname='{sensor}';")
    #         conn.commit()
    #     cursor.close()
    #     conn.close()

    def run(self):
        self.client.loop_forever()


if __name__ == '__main__':
    Sub(topic='iot/temp', username='emqx', password='public', host='39.98.122.153', port=1883, client_id='dasuidasdaj').start()
